/*
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.als;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;

import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.Grid;
import com.onegravity.sudoku.solver.data.Region;
import com.onegravity.sudoku.solver.util.BitSetIterator;

public class ALS implements Comparable<ALS> {
	
	private static final BitSetIterator<Integer> it = new BitSetIterator<Integer>();
	
	private final int counter;
	private final int degree;
	private final SortedSet<Cell> cells;
	private final BitSet allPotentials;
	private final Region region;

	private final NumberSet cellSet = new NumberSet();	// defines which of the 81 grid cells are part of the ALS
	private final Map<Integer, List<Cell>> value2Cells = new HashMap<Integer, List<Cell>>();
	
	private static final List<CommonCandidates> emptyCCList = Collections.emptyList(); 
	private List<CommonCandidates> commonCandidates = null; 

	ALS(Grid grid, int counter, int degree, SortedSet<Cell> cells, BitSet allPotentials, Region region) {
		this.counter = counter;
		this.degree = degree;
		this.cells = cells;
		this.allPotentials = allPotentials;
		this.region = region;

		// initialize cell set
		for (Cell cell:cells) {
			cellSet.add(cell.getIndex());
		}
		
		// initialize value 2 cell mapping
		synchronized(it) {
			for (it.restart(allPotentials); it.hasNext(); ) {
				Integer value = it.next();
				List<Cell> cell4Value = new ArrayList<Cell>();
				for (Cell cell:cells) {
					if (cell.hasPotentialValue(value)) {
						cell4Value.add(cell);
					}
				}
				value2Cells.put(value, cell4Value);
			}
		}

	}
	
	int getDegree() {
		return degree;
	}

	SortedSet<Cell> getCells() {
		return cells;
	}

	List<Cell> getCells(Integer value) {
		return value2Cells.get(value);
	}
	
	BitSet getPotentials() {
		return allPotentials;
	}

	boolean intersectsCell(ALS other) {
		return cellSet.intersects(other.cellSet);
	}

	boolean intersectsCandidates(ALS other) {
		return allPotentials.intersects(other.allPotentials);
	}

	Region getRegion() {
		return region;
	}

	int getCounter() {
		return counter;
	}

	void addCommonCandidates(CommonCandidates common) {
		if (commonCandidates == null) commonCandidates = new ArrayList<CommonCandidates>();
		commonCandidates.add(common);
	}

	public List<CommonCandidates> getCommonCandidates() {
		return (commonCandidates == null) ? emptyCCList : commonCandidates;
	}

	@Override
	public boolean equals(Object ob) {
		return (!(ob instanceof ALS)) ? false : compareTo((ALS) ob)==0;
	}

	@Override
	public int compareTo(ALS another) {
		if (degree!=another.degree) return (degree<another.degree) ? -1 : 1;
		return (cells.containsAll(another.cells)) ? 0 : -1;		// we don't need another.cells.containsAll(cells) because the two ALS have the same degree!
	}
	
	@Override
	public String toString() {
		return	"degree:     " + degree + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
				"cells:      " + Cell.toFullString(cells) + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
		   		"potentials: " + allPotentials; //$NON-NLS-1$
	}

}